/**********************************************************************************************/
/* FBL_Array.h                                                     					  		  */
/*                                                                       					  */
/* Copyright Paradigma, 1993-2006															  */
/* All Rights Reserved                                                   					  */
/**********************************************************************************************/

#ifndef _FBL_Array_h
	#define _FBL_Array_h

#include <VShared/FBL/publ/Headers/FBL.h>

#undef new
#include <algorithm>
#if DEBUG_NEW
#define new NEW
#endif // DEBUG_NEW

// FINAL:
#include <VShared/FBL/publ/Headers/FBL_pre_header.h>

#pragma warning(disable : 4996)

/**********************************************************************************************/
FBL_Begin_Namespace


/**********************************************************************************************/
template <class T>
struct NativeItems
{
	void init_elemenets( T* s, ulong count ) 		{ memset(s, 0, count * sizeof(T) ); }
	void destroy_elem( T elem ) 					{ argused1(elem); }
};


/**********************************************************************************************/
template <class T>
struct ClassItems
{
	void init_elemenets( T* s, ulong count )		{ argused2(s, count); }
	void destroy_elem( T elem ) 					{ argused1(elem); }
};


/**********************************************************************************************/
template <class T>
struct PtrItems
{
	void init_elemenets( T* s, ulong count ) 		{ memset(s, 0, count * sizeof(T) ); }
	void destroy_elem( T elem ) 					{ delete elem; }
};


/**********************************************************************************************/
template <class T>
struct SmartPtrItems
{
	void init_elemenets( T* s, ulong count )		{ argused2(s, count); }
	void destroy_elem( T& elem ) 					{ elem = 0; }
};


/**********************************************************************************************/
/** Remove policy used when item removed from array. For example, we can before remove item 
	from array call specified method. Or after remove write specified value to end of the array.
	Also this need if we have array of smart pointers - we must set write zero into last item.
	Because after copy existing elements method addref called and references counter changed.
		Example:
			We have array with 2 smart pointers
				     1			2			mItems = 2
				E1(mRefs=1), E2(mRefs=1)

			Delete first element. Second element go into first position. Method AddRef called.
			And we have next array
					1			2			mItems = 1
				E2(mRefs=2), E2(mRefs=2)

			We must put zero into elemnt 2.
			After do this we have good array.
					1			2			mItems = 1
				E1(mRefs=1), NULL	
*/


/**********************************************************************************************/
template <class T>
struct RPNothing
{
	void before_remove_elem( T& elem ) 				{ argused1(elem); }
	void after_remove_elem ( T* s, ulong count ) 	{ { argused2(s, count); } }
};

/**********************************************************************************************/
template <class T>
struct RPSetZero
{
	void before_remove_elem( T& elem ) 				{ argused1(elem); }
	void after_remove_elem ( T* s, ulong count ) 	{ s[ count - 1 ] = 0; }
};


/**********************************************************************************************/
enum Ownership
{
	kNotOwnItems 	= 0,	// == false
	kOwnItems 		= 1		// == true
};


/**********************************************************************************************/
// This is universal array template class.
// It is universal because it can be container of 
// 1) simple types (int, long, ...) 					FBL::Array<short> a(10);
// 2) pointers on simple types (int*, long*, ... )		FBL::Array<short*> a(10);
// 3) instances of classes ( A, STD::string, ... )		FBL::Array<A> a(10);
// 4) pointers of classes ( A*, STD::string*, ... ) 	FBL::Array<A*> a(10);
// 5) pointers of smart classes ( A_Ptr )				FBL::Array<A_Ptr> a(10);
//
// NOTE: IN THIS file are defined few helper TYPEDEFs below after class Array.
// NOTE: range of this class is 1-based.
//
template
<
	class T, 
	class elem_traits = PtrItems<T>,
	class remove_policy = RPNothing<T>
> 
class Array : 
	protected elem_traits, 
	protected remove_policy, 
	public I_Unknown
{
		IMPLEMENT_SINGLE_UNKNOWN(Array)		

	public://///////////////////////////////////////////////////////////////////////////////////
 
 		typedef T*       iterator;
 		typedef const T* const_iterator;


	public://///////////////////////////////////////////////////////////////////////////////////
 
 		// Construction:
 
							Array(
								ulong 		inInitialItemCount = 10,
								Ownership 	inOwnsItems = kNotOwnItems )
							{ 
								mData 		= NULL;
								mItems		= 0;
								mMaxItems	= inInitialItemCount;
								mOwnsItems	= inOwnsItems; 		

								if( mMaxItems )
								{
									mData = new T[mMaxItems];
									elem_traits::init_elemenets( mData, mMaxItems );
								}
							}
										
							Array( const Array& inOther );


	protected://////////////////////////////////////////////////////////////////////////////////

virtual 					~Array( void ) 
							{ 
								if( mOwnsItems )
									DisposeItems();

								if( mData )
									delete [] mData;
							}


	public://///////////////////////////////////////////////////////////////////////////////////

		void				DisposeItems( void ) throw();

							// make copy of this array on the heap and clone its items. 
		Array*				Clone( void );


	public://///////////////////////////////////////////////////////////////////////////////////

		// Accessories:

		ulong 				get_Count( void ) const throw()		{ return mItems; 	}	
		void				put_Count( ulong inCount ) throw()	{ mItems = inCount; } 
		
		ulong 				get_MaxCount( void ) const throw()	{ return mMaxItems;		}
				
		Ownership			get_OwnsItem( void ) const throw()	{ return mOwnsItems;	}
		void				put_OwnsItem( Ownership inOwns ) throw()	{ mOwnsItems = inOwns;	}
		
		const_iterator		begin( void ) const throw()			{ return mData; }
		const_iterator		end( void ) const throw() 			{ return mData + mItems; }


	public://///////////////////////////////////////////////////////////////////////////////////

		// Methods for items manipulations:

		void				AddItem( T inItem ) throw()
							{ 
								if( mItems == mMaxItems )
									Resize( mMaxItems ? mMaxItems * 2 : 10 );

								mData[mItems++] = inItem;
							}
		
		void				put_ArrayItem( T inItem, ulong inPos ) throw()
							{ 
								FBL_CHECK( inPos && inPos <= mItems );
								mData[inPos-1] = inItem; 		
							}
										
		void 				InsertAfter( T inNewItem, T inAfterItem ) throw()
							{
								// if inAfterItem is not found then we append a new item
								// to the end of the array.
								ulong afterIndex = inAfterItem ? FindIndex(inAfterItem) : mItems; 
								InsertAfterAt( inNewItem, afterIndex );
							}

		void 				InsertAfterAt( T inNewItem, ulong inAfterIndex ) throw();

							
		void				RemoveItem( T inItem, bool inDontDestroy = false ) throw()
							{ 
								ulong index = FindIndex(inItem);
								if( index > 0 )
									RemoveItemAt( index, inDontDestroy );	
							}		

		void				RemoveItemAt( ulong inPos, bool inDontDestroy = false ) throw();	

		const T&			get_ItemAt( ulong inPos ) const throw()
							{ 
								//FBL_CHECK( inPos && inPos <= mItems ); 
								return mData[inPos - 1];	
							}

		const T& 			FirstItem( void ) const throw()
							{ 
								FBL_CHECK( mItems > 0 );
								return mData[0]; 
							}
				
		const T&			LastItem( void ) const throw()
							{ 
								FBL_CHECK( mItems > 0 );
								return mData[mItems - 1]; 
							}
		
		ulong				FindIndex( const T inItem ) const throw(); 
		
		bool				Include( const T inItem ) const throw() 
								{ return bool(FindIndex(inItem) > 0); }		
	
		void				Resize( ulong inNewSize ) throw();

		void				SetItemsCount( ulong inItemsCount );


	private:////////////////////////////////////////////////////////////////////////////////////

		void				fromArray( const Array& inSrc );


	protected:////////////////////////////////////////////////////////////////////////////////// 

		T*					mData;

		ulong				mItems;
		ulong				mMaxItems;

		Ownership			mOwnsItems;
}; 


#pragma mark -
#pragma mark  === Specific classes


/**********************************************************************************************/
template<class T> 
class ArrayOfSimples : public Array<T, NativeItems<T>, RPNothing<T> >
{
	public://///////////////////////////////////////////////////////////////////////////////////
 
			ArrayOfSimples(
				ulong 		inInitialItemCount = 10,
				Ownership 	inOwnsItems = kNotOwnItems )
			:
			 	Array<T, NativeItems<T>, RPNothing<T> >( inInitialItemCount, inOwnsItems )	
			{	
			}


	protected://////////////////////////////////////////////////////////////////////////////////

virtual 	~ArrayOfSimples( void ) 
			{
			}
};


/**********************************************************************************************/
template<class T> 
class ArrayOfClasses : public Array<T, ClassItems<T>, RPNothing<T> >
{
	public://///////////////////////////////////////////////////////////////////////////////////
								
			ArrayOfClasses(
				ulong 		inInitialItemCount = 10,
				Ownership 	inOwnsItems = kNotOwnItems )
			:
			 	Array<T, ClassItems<T>, RPNothing<T> >( inInitialItemCount, inOwnsItems )	
			{	
			}	


	protected://////////////////////////////////////////////////////////////////////////////////

virtual		~ArrayOfClasses( void ) 
			{
			}
};


/**********************************************************************************************/
template<class T> 
class ArrayOfPtrs : public Array<T, PtrItems<T>, RPNothing<T> >
{
	public://///////////////////////////////////////////////////////////////////////////////////
 
			ArrayOfPtrs(
				ulong 		inInitialItemCount = 10,
				Ownership 	inOwnsItems = kNotOwnItems )
			:
			 	Array<T, PtrItems<T>, RPNothing<T> >( inInitialItemCount, inOwnsItems )	
			{	
			}	

	protected://////////////////////////////////////////////////////////////////////////////////

virtual 	~ArrayOfPtrs( void ) 
			{
			}
};


/**********************************************************************************************/
template<class T> 
class ArrayOfSmartPtrs : public Array<T, SmartPtrItems<T>, RPSetZero<T> >
{
	public://///////////////////////////////////////////////////////////////////////////////////
 
			ArrayOfSmartPtrs(
				ulong 		inInitialItemCount = 10,
				Ownership 	inOwnsItems = kOwnItems )
			:
			 	Array<T, SmartPtrItems<T>, RPSetZero<T> >( inInitialItemCount, inOwnsItems )	
			{	
			}

	protected://////////////////////////////////////////////////////////////////////////////////

virtual 	~ArrayOfSmartPtrs( void ) 
			{
			}
};


#pragma mark -
#pragma mark  === inline methods


/**********************************************************************************************/
template <class T, class elem_traits, class remove_policy >
Array<T, elem_traits, remove_policy>::Array( const Array<T, elem_traits, remove_policy>& inSrc )
{
	mOwnsItems 	= kNotOwnItems;
	mItems		= inSrc.mItems;
	mMaxItems	= inSrc.mMaxItems;

	mData = new T [mMaxItems];

	for( ulong i = 1; i <= mItems; ++i )
	{
		mData[i-1] = inSrc.get_ItemAt(i);
	}
}


/**********************************************************************************************/
template <class T, class elem_traits, class remove_policy >
void Array<T, elem_traits, remove_policy>::DisposeItems( void ) throw()
{ 
	if( mOwnsItems )
	{
		for( ushort i = 0; i < mItems ; ++i )
		{
			elem_traits::destroy_elem( mData[i] ); 
		}
	}

	delete [] mData;
	mData = NULL;

	mItems = 0;
	mMaxItems = 0;
}



/**********************************************************************************************/
template <class T, class elem_traits, class remove_policy >
void Array<T, elem_traits, remove_policy>::InsertAfterAt( T inNewItem, ulong inAfterIndex ) throw()
{
	if( !(0 <= inAfterIndex && inAfterIndex <= mItems) )
		inAfterIndex = mItems;

	if( mItems == mMaxItems )
		Resize( mMaxItems ? mMaxItems * 2 : 10  );

	std::copy_backward( mData + inAfterIndex, mData + mItems, mData + mItems + 1 );

	mData[inAfterIndex] = inNewItem;
	mItems++;
}


/**********************************************************************************************/
template <class T, class elem_traits, class remove_policy >
void Array<T, elem_traits, remove_policy>::RemoveItemAt( ulong pos, bool inDontDestroy ) throw()
{ 
	argused1( inDontDestroy );

	//FBL_CHECK( pos && pos <= mItems  ); 

	if( pos == 0 || pos > mItems )
		return;	// nothing to remove

	remove_policy::before_remove_elem( mData[pos-1] );

	if( mOwnsItems && inDontDestroy == false )
	{	
		elem_traits::destroy_elem( mData[pos-1] );
	}

	std::copy( mData + pos, mData + mItems, mData + pos - 1 );

	remove_policy::after_remove_elem( mData, mItems );

	mItems--;
}				


/**********************************************************************************************/
template <class T, class elem_traits, class remove_policy >
ulong Array<T, elem_traits, remove_policy>::FindIndex( const T inItem ) const throw()
{
	T* pItem	= mData;
	T* pEndItem	= mData + mItems;

	while( pItem < pEndItem )
	{
		if( *pItem == inItem )
			return (ulong)(pItem - mData + 1);

		++pItem;
	}

	return 0L;	// bad index.
}


/**********************************************************************************************/
template <class T, class elem_traits, class remove_policy >
void Array<T, elem_traits, remove_policy>::Resize( ulong inNewSize ) throw()
{
	if( inNewSize == 0 && mData ) // total colapse.
	{
		if( mOwnsItems )
			DisposeItems();
	
		delete [] mData;
		mData = NULL;
	
		mItems = 0;
		mMaxItems = 0;
	}
	else // can be as greater so smaller
	{
		T* pNewArray = new T [inNewSize];
		ulong ToCopy = mItems < inNewSize ? mItems : inNewSize; 

		if( mData )
		{
			STD::copy( begin(), begin() + ToCopy, pNewArray );

			delete [] mData;
			mData = NULL;
		}

		// fill by zero rest items if any. 
		elem_traits::init_elemenets( pNewArray + ToCopy, inNewSize - ToCopy );

		mData = pNewArray;
		mItems = mItems < inNewSize ? mItems : inNewSize;
		mMaxItems = inNewSize;
	}
}


/**********************************************************************************************/
// This function allow easy change 
//
template <class T, class elem_traits, class remove_policy >
void Array<T, elem_traits, remove_policy>::SetItemsCount( ulong inItemsCount )
{
	if( inItemsCount > mMaxItems )	
	{
		Resize( inItemsCount );
	}
	else if( inItemsCount < mMaxItems )	 // we reduce the number of items.
	{
		if( mOwnsItems )
		{
			for( ushort i = (ushort) inItemsCount; i < mItems ; ++i ) // use zero-based indexes.
			{
				elem_traits::destroy_elem( mData[i] ); 
			}
		}

	}
		
	mItems = inItemsCount;
}


/**********************************************************************************************/
FBL_End_Namespace

#include <VShared/FBL/publ/Headers/FBL_post_header.h>

#endif // _FBL_Array_h
